Utility Modules
**Referenced Files in This Document** - [theme-toggling.js](file://src/assets/js/modules/theme-toggling.js) - [search-functionality.js](file://src/assets/js/modules/search-functionality.js) - [newsletter-spam-protection.js](file://src/assets/js/modules/newsletter-spam-protection.js) - [scroll-nav-polish.js](file://src/assets/js/modules/scroll-nav-polish.js) - [custom-cursor.js](file://src/assets/js/modules/custom-cursor.js) - [material-design-3-main-theme-toggle.js](file://src/assets/js/modules/material-design-3-main-theme-toggle.js) - [iaa-alliance-theme-toggle.js](file://src/assets/js/modules/iaa-alliance-theme-toggle.js) - [main.js](file://src/assets/js/main.js) - [base.njk](file://src/_includes/layouts/base.njk) - [38-material-design-3-theme-toggle.css](file://src/assets/css/modules/38-material-design-3-theme-toggle.css) - [41-search-modal.css](file://src/assets/css/modules/41-search-modal.css) - [03-custom-cursor.css](file://src/assets/css/modules/03-custom-cursor.css)Table of Contents
- Introduction
- Project Structure
- Core Components
- Architecture Overview
- Detailed Component Analysis
- Dependency Analysis
- Performance Considerations
- Troubleshooting Guide
- Conclusion
Introduction
This document provides comprehensive documentation for the utility JavaScript modules that power specialized functionality across the site. It covers:
- Automatic theme switching using IntersectionObserver
- Instant client-side search powered by PageFind with debouncing and result filtering
- Form validation and spam prevention for ConvertKit newsletter forms
- Enhanced navigation scrolling behavior and smooth anchor links
- Custom mouse cursor effects with GSAP and hover states
Each module’s API patterns, event handling, state management, configuration options, integration patterns, performance characteristics, browser compatibility, fallback mechanisms, and troubleshooting guidance are explained with practical examples and diagrams.
Project Structure
The utility modules live under the JavaScript modules directory and are initialized in the main entry point. They integrate with HTML templates and CSS modules to deliver cohesive user experiences.
graph TB
subgraph "JavaScript Modules"
TT["theme-toggling.js"]
SF["search-functionality.js"]
NSP["newsletter-spam-protection.js"]
SNav["scroll-nav-polish.js"]
CC["custom-cursor.js"]
MDT["material-design-3-main-theme-toggle.js"]
IAT["iaa-alliance-theme-toggle.js"]
end
subgraph "Entry Point"
MAIN["main.js"]
end
subgraph "Templates"
BASE["base.njk"]
end
subgraph "CSS Modules"
TCSS["38-material-design-3-theme-toggle.css"]
SCSS["41-search-modal.css"]
CCSS["03-custom-cursor.css"]
end
MAIN --> TT
MAIN --> SF
MAIN --> NSP
MAIN --> SNav
MAIN --> CC
MAIN --> MDT
MAIN --> IAT
BASE --> SF
BASE --> CC
BASE --> MDT
BASE --> IAT
SF --> SCSS
CC --> CCSS
MDT --> TCSS
IAT --> TCSS
Diagram sources
- [main.js:15-36](file://src/assets/js/main.js#L15-L36)
- [base.njk:87-127](file://src/_includes/layouts/base.njk#L87-L127)
- [38-material-design-3-theme-toggle.css:1-237](file://src/assets/css/modules/38-material-design-3-theme-toggle.css#L1-L237)
- [41-search-modal.css:1-314](file://src/assets/css/modules/41-search-modal.css#L1-L314)
- [03-custom-cursor.css:1-46](file://src/assets/css/modules/03-custom-cursor.css#L1-L46)
Section sources
- [main.js:1-37](file://src/assets/js/main.js#L1-L37)
- [base.njk:1-260](file://src/_includes/layouts/base.njk#L1-L260)
Core Components
- Theme toggling with IntersectionObserver for automatic theme switching based on viewport position
- PageFind-powered search with debouncing, keyboard navigation, and result filtering
- ConvertKit newsletter spam protection via honeypot and submission timing checks
- Navigation polish for responsive shadow and padding adjustments on scroll
- Custom cursor with GSAP animation and hover states
Section sources
- [theme-toggling.js:1-24](file://src/assets/js/modules/theme-toggling.js#L1-L24)
- [search-functionality.js:1-179](file://src/assets/js/modules/search-functionality.js#L1-L179)
- [newsletter-spam-protection.js:1-24](file://src/assets/js/modules/newsletter-spam-protection.js#L1-L24)
- [scroll-nav-polish.js:1-19](file://src/assets/js/modules/scroll-nav-polish.js#L1-L19)
- [custom-cursor.js:1-28](file://src/assets/js/modules/custom-cursor.js#L1-L28)
Architecture Overview
The initialization flow wires all utilities together. The base template provides markup for search and theme toggle controls. CSS modules define theme-aware styles and modal presentation.
sequenceDiagram
participant DOM as "DOM Ready"
participant Main as "main.js"
participant Nav as "initNavigation()"
participant ThemeObs as "initThemeToggling()"
participant Polish as "initScrollPolish()"
participant ThemeUI as "initThemeToggle()"
participant Search as "initSearch()"
participant Spam as "initNewsletterSpamProtection()"
participant Cursor as "initCustomCursor()"
DOM->>Main : "DOMContentLoaded"
Main->>Nav : "Initialize navigation"
Main->>ThemeObs : "Setup intersection observer"
Main->>Polish : "Attach scroll listener"
Main->>ThemeUI : "Bind theme toggle UI"
Main->>Search : "Bind search modal and PageFind"
Main->>Spam : "Attach honeypot and timing checks"
Main->>Cursor : "Initialize custom cursor (GSAP)"
Diagram sources
- [main.js:15-36](file://src/assets/js/main.js#L15-L36)
- [base.njk:87-127](file://src/_includes/layouts/base.njk#L87-L127)
Detailed Component Analysis
Theme Toggling (IntersectionObserver)
Automatically switches themes based on which section enters the viewport. Uses IntersectionObserver with a centered root margin to detect when a section becomes dominant.
Key behaviors:
- Observes elements with a data-theme attribute
- Updates body classes to reflect light/dark backgrounds
- Prevents redundant theme toggles by tracking the current theme
flowchart TD
Start(["Init Theme Toggling"]) --> Query["Query sections with data-theme"]
Query --> HasSections{"Any sections found?"}
HasSections --> |No| Exit["Return early"]
HasSections --> |Yes| Observe["Create IntersectionObserver"]
Observe --> Loop["On each entry"]
Loop --> Intersect{"Is intersecting?"}
Intersect --> |No| Loop
Intersect --> |Yes| GetTheme["Get dataset.theme"]
GetTheme --> Same{"Same as current?"}
Same --> |Yes| Loop
Same --> |No| Update["Update current and body classes"]
Update --> Loop
Diagram sources
- [theme-toggling.js:3-21](file://src/assets/js/modules/theme-toggling.js#L3-L21)
Integration highlights:
- Sections must carry the data-theme attribute to participate
- Body classes toggle on-light-bg/on-dark-bg to drive CSS color schemes
- Root margin set to "-50% 0px -50% 0px" ensures detection when a section occupies the viewport center
Section sources
- [theme-toggling.js:1-24](file://src/assets/js/modules/theme-toggling.js#L1-L24)
- [base.njk:63](file://src/_includes/layouts/base.njk#L63)
Search Functionality (PageFind)
Provides instant client-side search with debouncing, keyboard navigation, and result categorization. Integrates with PageFind for indexing and querying.
Key behaviors:
- Loads PageFind dynamically and sets excerpt length
- Opens/closes modal with focus management and overflow control
- Debounces input events at 200ms
- Supports keyboard navigation (arrow keys, Enter, Escape)
- Clears input and resets selection state
- Categorizes results by URL path prefixes
sequenceDiagram
participant User as "User"
participant UI as "Search UI"
participant PF as "PageFind"
participant DOM as "DOM"
User->>UI : "Click search toggle"
UI->>UI : "openModal()"
UI->>PF : "loadPagefind() (async import)"
PF-->>UI : "PageFind instance"
User->>UI : "Type query"
UI->>UI : "Debounce 200ms"
UI->>PF : "performSearch(query)"
PF-->>UI : "results"
UI->>DOM : "Render results list"
User->>UI : "Arrow keys / Enter / Escape"
UI->>DOM : "Update selection / navigate / close"
Diagram sources
- [search-functionality.js:3-176](file://src/assets/js/modules/search-functionality.js#L3-L176)
- [base.njk:100-127](file://src/_includes/layouts/base.njk#L100-L127)
Implementation notes:
- Uses async import for PageFind to minimize initial bundle size
- Limits results to 10 items per query
- Provides category icons and metadata derived from URL segments
- Keyboard shortcuts: Ctrl/Cmd+K to open, arrow keys to navigate, Enter to select, Escape to close
Section sources
- [search-functionality.js:1-179](file://src/assets/js/modules/search-functionality.js#L1-L179)
- [41-search-modal.css:1-314](file://src/assets/css/modules/41-search-modal.css#L1-L314)
Newsletter Spam Protection
Adds a hidden honeypot field to ConvertKit forms and prevents submission if the field is filled or if the form is submitted too quickly after page load.
Key behaviors:
- Detects forms targeting ConvertKit
- Injects a hidden input named _gotcha
- Validates submission timing (minimum 3 seconds after load)
- Prevents submission if honeypot has content or submission is too fast
flowchart TD
Start(["Form Submit Event"]) --> CheckHoneypot["Check _gotcha value"]
CheckHoneypot --> FastSubmit{"Submitted < 3s after load?"}
CheckHoneypot --> |Yes| Block["Prevent default"]
FastSubmit --> |Yes| Block
FastSubmit --> |No| Allow["Allow submission"]
CheckHoneypot --> |No| FastSubmit
Diagram sources
- [newsletter-spam-protection.js:3-24](file://src/assets/js/modules/newsletter-spam-protection.js#L3-L24)
Section sources
- [newsletter-spam-protection.js:1-24](file://src/assets/js/modules/newsletter-spam-protection.js#L1-L24)
Scroll Navigation Polish
Enhances navigation responsiveness by adjusting padding and shadow on scroll, improving readability and depth perception.
Key behaviors:
- Listens to scroll events with passive option for performance
- Applies padding and box-shadow when scrollY exceeds 80px
- Resets styles when scrolled back to top
flowchart TD
Start(["Scroll Event"]) --> Check["Check scrollY > 80"]
Check --> |Yes| Apply["Apply padding and shadow"]
Check --> |No| Reset["Reset styles"]
Apply --> End(["Done"])
Reset --> End
Diagram sources
- [scroll-nav-polish.js:3-16](file://src/assets/js/modules/scroll-nav-polish.js#L3-L16)
Section sources
- [scroll-nav-polish.js:1-19](file://src/assets/js/modules/scroll-nav-polish.js#L1-L19)
Custom Cursor
Delivers a desktop-only animated cursor using GSAP with two layers: a small primary cursor and a larger follower. Adjusts appearance based on theme and element hover states.
Key behaviors:
- Detects coarse pointers and small screens to disable cursor
- Initializes GSAP tweens for smooth movement
- Adds hover classes to both cursor elements on interactive elements
- Uses CSS variables to adapt colors to theme
sequenceDiagram
participant Window as "Window"
participant Cursor as "Custom Cursor"
participant GSAP as "GSAP"
participant DOM as "Interactive Elements"
Window->>Cursor : "initCustomCursor()"
Cursor->>Cursor : "Check pointer/coarse screen"
Cursor->>GSAP : "Set initial positions"
Window->>Cursor : "mousemove"
Cursor->>GSAP : "Tween cursor and follower"
DOM->>Cursor : "mouseenter/mouseleave"
Cursor->>DOM : "Add/remove hover classes"
Diagram sources
- [custom-cursor.js:3-25](file://src/assets/js/modules/custom-cursor.js#L3-L25)
- [03-custom-cursor.css:1-46](file://src/assets/css/modules/03-custom-cursor.css#L1-L46)
Section sources
- [custom-cursor.js:1-28](file://src/assets/js/modules/custom-cursor.js#L1-L28)
- [03-custom-cursor.css:1-46](file://src/assets/css/modules/03-custom-cursor.css#L1-L46)
Dependency Analysis
- Initialization order: main.js orchestrates module initialization and conditionally enables GSAP-dependent features
- Template integration: base.njk provides the search modal and theme toggle UI elements
- CSS integration: theme toggle and search modal styles are defined in dedicated CSS modules
- Cross-module dependencies:
- theme-toggling.js relies on sections with data-theme attributes
- search-functionality.js depends on PageFind availability and DOM elements with specific IDs/classes
- custom-cursor.js depends on GSAP and specific DOM elements for cursor containers
graph LR
MAIN["main.js"] --> TT["theme-toggling.js"]
MAIN --> SF["search-functionality.js"]
MAIN --> NSP["newsletter-spam-protection.js"]
MAIN --> SNav["scroll-nav-polish.js"]
MAIN --> CC["custom-cursor.js"]
MAIN --> MDT["material-design-3-main-theme-toggle.js"]
MAIN --> IAT["iaa-alliance-theme-toggle.js"]
BASE["base.njk"] --> SF
BASE --> CC
BASE --> MDT
BASE --> IAT
SF --> SCSS["41-search-modal.css"]
CC --> CCSS["03-custom-cursor.css"]
MDT --> TCSS["38-material-design-3-theme-toggle.css"]
IAT --> TCSS
Diagram sources
- [main.js:1-37](file://src/assets/js/main.js#L1-L37)
- [base.njk:87-127](file://src/_includes/layouts/base.njk#L87-L127)
- [41-search-modal.css:1-314](file://src/assets/css/modules/41-search-modal.css#L1-L314)
- [03-custom-cursor.css:1-46](file://src/assets/css/modules/03-custom-cursor.css#L1-L46)
- [38-material-design-3-theme-toggle.css:1-237](file://src/assets/css/modules/38-material-design-3-theme-toggle.css#L1-L237)
Section sources
- [main.js:1-37](file://src/assets/js/main.js#L1-L37)
- [base.njk:1-260](file://src/_includes/layouts/base.njk#L1-L260)
Performance Considerations
- IntersectionObserver: Uses a centered root margin to detect dominant sections efficiently; thresholds are minimal to reduce churn
- Debouncing: Search input is debounced at 200ms to balance responsiveness and performance
- Passive listeners: Scroll listener uses passive option to improve scroll performance
- Lazy loading: PageFind is dynamically imported to avoid blocking initial load
- Conditional features: Custom cursor and advanced animations are disabled on coarse-pointer devices or below a certain viewport width
- Local storage: Theme preferences are cached locally to avoid repeated DOM manipulation on subsequent visits
[No sources needed since this section provides general guidance]
Troubleshooting Guide
Common issues and resolutions:
- Search modal does not open:
- Verify presence of .search-toggle and .search-modal elements
- Confirm PageFind script is accessible and importable
- Check that the modal is appended to the DOM by the base template
- Search results not appearing:
- Ensure PageFind is loaded and configured; confirm excerpt length and search results are populated
- Validate that the input element has the correct ID and is focused on open
- Newsletter form submits despite spam protection:
- Confirm the hidden _gotcha field is present and not visible
- Ensure submission occurs at least 3 seconds after page load
- Navigation polish not applying:
- Confirm the .nav element exists and scroll events fire
- Check for CSS conflicts that override padding/shadow
- Custom cursor not moving:
- Verify GSAP is loaded and available
- Ensure .cursor and .cursor-follower elements exist
- Confirm device is not coarse-pointer or below the minimum width threshold
Section sources
- [search-functionality.js:3-176](file://src/assets/js/modules/search-functionality.js#L3-L176)
- [newsletter-spam-protection.js:3-24](file://src/assets/js/modules/newsletter-spam-protection.js#L3-L24)
- [scroll-nav-polish.js:3-16](file://src/assets/js/modules/scroll-nav-polish.js#L3-L16)
- [custom-cursor.js:3-25](file://src/assets/js/modules/custom-cursor.js#L3-L25)
- [base.njk:87-127](file://src/_includes/layouts/base.njk#L87-L127)
Conclusion
These utility modules collectively enhance usability, accessibility, and interactivity across the site. They demonstrate robust patterns for initialization, event handling, state management, and graceful degradation. By following the integration guidelines and troubleshooting tips, developers can confidently extend or customize these utilities while maintaining performance and compatibility.
[No sources needed since this section summarizes without analyzing specific files]